package com.test.PSO;

import com.test.BRBInference.BRBInference;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

public class PSO {
    private int c1 = 2;
    private int c2 = 2;

    private int maxgen = 100;
    private int sizepop = 20;

    private ArrayList<Double> ub = new ArrayList<>();
    private ArrayList<Double> lb = new ArrayList<>();

    private ArrayList<Double> Vmax = new ArrayList<>();
    private ArrayList<Double> Vmin = new ArrayList<>();

    private ArrayList<Double> dataArray;
    private ArrayList<ArrayList<Double>> modelArray;
    private ArrayList<Double> uscale = new ArrayList<>();

    private ArrayList<ArrayList<Double>> pop = new ArrayList<>();
    private ArrayList<ArrayList<Double>> V = new ArrayList<>();

    private Double zbestfitness = 0.0; //全局最佳
    private ArrayList<Double> zbest = new ArrayList<>();

    private ArrayList<Double> gbestfitness = new ArrayList<>(); //个体最佳
    private ArrayList<ArrayList<Double>> gbest = new ArrayList<>();

    private ArrayList<Integer> attrArray;

    public PSO(ArrayList<Double> uscale ,ArrayList<Double> dataArray, ArrayList<ArrayList<Double>> modelArray, ArrayList<Integer> attrArray) {
        this.dataArray = dataArray;
        this.modelArray = modelArray;
        this.attrArray = attrArray;
        this.uscale = uscale;
    }

    public Double getZbestfitness() {
        return zbestfitness;
    }

    public ArrayList<Double> getZbest() {
        return zbest;
    }

    public Double fitness(ArrayList<Double> single_pop) throws IOException {
        Double Fitness;
        ArrayList<ArrayList<Double>> data_temp = new ArrayList<>();
        data_temp.add(single_pop);

        BRBInference BRB = new BRBInference(uscale, data_temp, modelArray);
        BRB.attr_identify();
        BRB.fuse();

        Fitness = BRB.abs(BRB.getInference_Result().get(0));

        return Fitness;
    }


    public void Init() throws IOException {
        for (int i = 0; i < dataArray.size()-1; ++i) {
            ub.add(modelArray.get(modelArray.size() - 1).get(i + 1));
            lb.add(modelArray.get(0).get(i + 1));
        }

        for(int i=0;i<ub.size();++i) {
            Vmax.add((ub.get(i) - lb.get(i))*0.5);
            Vmin.add(-(ub.get(i) - lb.get(i))*0.5);
        }

        for (int k = 0; k < sizepop; ++k) {
            ArrayList<Double> Temp = new ArrayList<>();
            for(Double value : dataArray) {
                Temp.add(value);
            }
            pop.add(Temp);
        }

        ArrayList<Double> Fitness = new ArrayList<>();
        for (int k = 0; k < sizepop; ++k) {
            ArrayList<Double> v = new ArrayList<>();
            for (int i = 0; i < attrArray.size(); ++i) {
                Double ub_temp = ub.get(attrArray.get(i));
                Double lb_temp = lb.get(attrArray.get(i));
                double tmp = new Random().nextDouble();
                if(k!=0) {
                    double temp = lb_temp + (ub_temp - lb_temp) * tmp;
                    pop.get(k).set(attrArray.get(i), temp);
                }
                v.add((ub_temp - lb_temp) * tmp * 0.1);
            }
            V.add(v);
            Double fitness = fitness(pop.get(k));
            gbestfitness.add(fitness);
            gbest.add(pop.get(k));
            Fitness.add(fitness);
        }


        int min_index = BRBInference.find_min(Fitness);
        zbestfitness = BRBInference.min(Fitness);
        for(int i=0;i<pop.get(min_index).size();++i) {
            zbest.add(pop.get(min_index).get(i));
        }

        System.out.println();
    }

    public void Process() throws IOException {

        for(int i=0;i<maxgen;++i) {

            ArrayList<Double> Fitness = new ArrayList<>();
            for(int j=0;j<sizepop;++j) {
                for(int z=0;z<attrArray.size();++z) {
                    double tmp = new Random().nextDouble();
                    double v = V.get(j).get(z) * 0.8 + c1 * tmp * (gbest.get(j).get(attrArray.get(z)) - pop.get(j).get(attrArray.get(z))) + c2 * tmp * (zbest.get(attrArray.get(z)) - pop.get(j).get(attrArray.get(z)));
                    if (v > Vmax.get(attrArray.get(z))) {
                        v = Vmax.get(attrArray.get(z));
                    }
                    if (v < Vmin.get(attrArray.get(z))) {
                        v = Vmin.get(attrArray.get(z));
                    }
                    V.get(j).set(z, v);
                    double temp = pop.get(j).get(attrArray.get(z)) + v;
                    if(temp > ub.get(attrArray.get(z))) {
                        temp = ub.get(attrArray.get(z));
                    }

                    if(temp < lb.get(attrArray.get(z))) {
                        temp = lb.get(attrArray.get(z));
                    }

                    pop.get(j).set(attrArray.get(z),temp);
                }
                Double fitness = fitness(pop.get(j));
                Fitness.add(fitness);
            }

            for(int j=0;j<sizepop;++j) {
                if(Fitness.get(j) < gbestfitness.get(j)) {
                    gbestfitness.set(j,Fitness.get(j));
                    gbest.set(j,pop.get(j));
                }
                if(Fitness.get(j) < zbestfitness) {
                    zbestfitness = Fitness.get(j);
                    for(int k=0;k<zbest.size();++k) {
                        zbest.set(k,pop.get(j).get(k));
                    }
                }
            }
        }
        System.out.println();
    }

}
